import Geetest from 'geetest';
import env from 'env';
import { isObject, find } from 'lodash';
import request from 'request';
import crypto from 'crypto';

class WechatApp {
    constructor(opts) {
        this.name = opts.name;
        this.appID = opts.appID;
        this.appSecret = opts.appSecret;
    }

    async getAccessToken() {
        if (isObject(this._accessToken) &&
            this._accessToken.expireAt <= Date.now()) {

            return this._accessToken.data;
        }

        return request({
            method: 'GET',
            url: 'https://api.weixin.qq.com/cgi-bin/token',
            json: true,
            qs: {
                grant_type: 'client_credential',
                appid: this.appID,
                secret: this.appSecret
            }
        })
        .then(resp => {
            this._accessToken = {
                data: resp.access_token,
                expireAt: Date.now() + resp.expires_in
            };

            return this._accessToken.data;
        });
    }

    async getTicket(accessToken) {
        if (isObject(this._ticket) &&
            this._ticket.expireAt <= Date.now()) {

            return this._ticket.data;
        }

        return request({
            method: 'GET',
            url: 'https://api.weixin.qq.com/cgi-bin/ticket/getticket',
            json: true,
            qs: {
                access_token: accessToken,
                type: 'jsapi',
            }
        })
        .then(resp => {
            this._ticket = {
                data: resp.ticket,
                expireAt: Date.now() + resp.expires_in
            };

            return this._ticket.data;
        });
    }
}

const configs = [
    env.WECHAT_CONFIG_0,
    env.WECHAT_CONFIG_1,
    env.WECHAT_CONFIG_2
].map(configStr => {
    let parts = configStr.split(',');
    return {
        name: parts[0],
        appID: parts[1],
        appSecret: parts[2]
    };
});

const appsDefined = configs.map(opts => new WechatApp(opts));
const timestamp = Date.now();

/**
 * JSSDK 签名
 * @param  {Context} ctx
 * @return {Object}
 */
export async function signTicket(ctx) {
    const {
        appid, url
    } = ctx.query;
    const nonceStr = 'abcdefghijk';
    const app = find(appsDefined, ['appID', appid]);
    let token, ticket, shasum;

    try {
        token = await app.getAccessToken();
    } catch (e) {
        // 非真正 ErrorCode
        ctx.set('ErrorCode', '50001');
        ctx.body = {
            mess: '获取 AccessToken 失败',
            cause: e.message
        };

        return;
    }

    try {
        ticket = await app.getTicket(token);
    } catch (e) {
        // 非真正 ErrorCode
        ctx.set('ErrorCode', '50002');
        ctx.body = {
            mess: '获取 Ticket 失败',
            cause: e.message
        };

        return;
    }

    // 签名
    shasum = crypto.createHash('sha1');
    shasum.update('jsapi_ticket=' + ticket +
        '&noncestr=' + nonceStr +
        '&timestamp=' + timestamp +
        '&url=' + url);

    ctx.body = {
        mess: '签名成功',
        nonceStr,
        signature: shasum.digest('hex'),
        timestamp
    };
}
